﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;

namespace SplendidCRM.Ext.Helpers
{
    public enum FieldType
    {
        [FieldTag("input", "type", "button", "class", "form-button")]
        Button,
        [FieldTag("input", "type", "checkbox")]
        Checkbox,
        [FieldTag("input", "type", "color")]
        Color,
        [FieldTag("input", "type", "date", "class", "form-date")]
        Date,
        [FieldTag("input", "type", "datetime", "class", "form-datetime")]
        DateTime,
        [FieldTag("input", "type", "datetime-local", "class", "form-datetimelocal")]
        DateTimeLocal,
        [FieldTag("input", "type", "email", "class", "form-email")]
        Email,
        [FieldTag("input", "type", "file", "class", "form-file")]
        File,
        [FieldTag("input", "type", "hidden", "fieldContainer", false, "fieldWrapper", false)]
        Hidden,
        [FieldTag("input", "type", "image", "class", "form-image")]
        Image,
        [FieldTag("input", "type", "month", "class", "form-month")]
        Month,
        [FieldTag("input", "type", "number", "class", "form-number")]
        Number,
        [FieldTag("input", "type", "password", "class", "form-password")]
        Password,
        [FieldTag("input", "type", "radio", "class", "form-radio")]
        Radio,
        [FieldTag("input", "type", "range", "class", "form-range")]
        Range,
        [FieldTag("input", "type", "reset", "class", "form-button form-reset")]
        Reset,
        [FieldTag("input", "type", "search", "class", "form-search")]
        Search,
        [FieldTag("input", "type", "submit", "class", "form-button form-submit")]
        Submit,
        [FieldTag("input", "type", "tel", "class", "form-tel")]
        Tel,
        [FieldTag("input", "type", "text", "class", "form-text")]
        Text,
        [FieldTag("input", "type", "time", "class", "form-time")]
        Time,
        [FieldTag("input", "type", "url", "class", "form-url")]
        Url,
        [FieldTag("input", "type", "week", "class", "form-week")]
        Week,
        [FieldTag("textarea", false, "class", "form-text")]
        TextArea,
        [FieldTag("select", false, "class", "form-dropdown")]
        Dropdown,
        [FieldTag("select", false, "multiple", "multiple", "class", "form-list")]
        List,
        [FieldTag("span", false)]
        Span,
        [FieldTag("div", false)]
        Div,
        [FieldTag("p", false)]
        Paragraph,
        [FieldTag("label", false)]
        Label
    }

    [AttributeUsage(AttributeTargets.Field)]
    class FieldTagAttribute : Attribute
    {
        public FieldTagAttribute(String name, params  Object[] attributes)
            : this(name, true, attributes)
        {
        }

        public FieldTagAttribute(String name, Boolean selfClosing, params  Object[] attributes)
        {
            Name = name;
            SelfClosing = selfClosing;
            Attributes = new Dictionary<String, Object>(StringComparer.OrdinalIgnoreCase);
            for (var i = 0; i < attributes.Length - 1; i += 2)
            {
                var key = attributes[i];
                if (key == null) continue;
                Attributes[key.ToString()] = attributes[i + 1];
            }
        }

        public String Name { get; private set; }
        public Boolean SelfClosing { get; private set; }
        public IDictionary<String, Object> Attributes { get; private set; }
    }

    public static class FormHtmlHelper
    {
        public static HtmlString Field(this HtmlHelper helper, String tag)
        {
            return Field(helper, tag, null);
        }

        public static HtmlString Field(this HtmlHelper helper, String tag, Object attributes)
        {
            return Field(helper, tag, attributes, null);
        }

        public static HtmlString Field(this HtmlHelper helper, String tag, Object attributes, Object dataBind)
        {
            return Field(new FieldTagAttribute(tag, true, "fieldcontainer", false, "fieldwrapper", false), attributes, dataBind);
        }

        public static HtmlString Field(this HtmlHelper helper, FieldType type)
        {
            return Field(helper, type, null);
        }

        public static HtmlString Field(this HtmlHelper helper, FieldType type, Object attributes)
        {
            return Field(helper, type, attributes, null);
        }

        public static HtmlString Field(this HtmlHelper helper, FieldType type, Object attributes, Object dataBind)
        {
            return Field(typeof (FieldType).GetField(type.ToString(), BindingFlags.Static | BindingFlags.Public).GetCustomAttributes(typeof (FieldTagAttribute), true)[0] as FieldTagAttribute, attributes, dataBind);
        }

        private static HtmlString Field(FieldTagAttribute tag, Object attributes, Object dataBind)
        {
            var stringWriter = new StringWriter();
            var htmlWriter = new HtmlTextWriter(stringWriter);
            var attrDic = new Dictionary<String, Object>(StringComparer.OrdinalIgnoreCase);
            Object innerHtml, innerText, fieldLabel = null, fieldContainer, fieldWrapper, fieldClass, containerClass, labelClass;

            foreach (var attribute in tag.Attributes)
            {
                attrDic[attribute.Key] = attribute.Value;
            }

            if (attributes is String)
            {
                fieldLabel = attributes;
            }
            else
            {
                foreach (var attribute in Mvc.GetRouteValues(attributes))
                {
                    attrDic[attribute.Key] = attribute.Value;
                }
            }

            if (dataBind is String)
            {
                attrDic["data-bind"] = dataBind;
            }
            else if (dataBind != null)
            {
                attrDic["data-bind"] = String.Join(",", Mvc.GetRouteValues(dataBind).Select(p =>
                                                                                                {
                                                                                                    var value = p.Value;
                                                                                                    if (value is Boolean) value = (Boolean) value ? "true" : "false";
                                                                                                    return String.Format("{0}: {1}", p.Key.Json(), value);
                                                                                                }).ToArray());
            }


            if (attrDic.TryGetValue("containerclass", out containerClass)) attrDic.Remove("containerclass");
            if (attrDic.TryGetValue("labelclass", out labelClass)) attrDic.Remove("labelclass");
            if (attrDic.TryGetValue("fieldclass", out fieldClass)) attrDic.Remove("fieldclass");

            if (attrDic.TryGetValue("innerhtml", out innerHtml))
            {
                if (innerHtml == null) innerHtml = String.Empty;
                attrDic.Remove("innerhtml");
            }

            if (attrDic.TryGetValue("innertext", out innerText))
            {
                if (innerText == null) innerText = String.Empty;
                attrDic.Remove("innertext");
            }

            if (fieldLabel == null && attrDic.TryGetValue("fieldlabel", out fieldLabel))
            {
                if (fieldLabel == null) fieldLabel = String.Empty;
                attrDic.Remove("fieldlabel");
            }

            if (attrDic.TryGetValue("fieldcontainer", out fieldContainer))
            {
                if (fieldContainer == null) fieldContainer = false;
                attrDic.Remove("fieldcontainer");
            }
            else
            {
                fieldContainer = true;
            }
            
            if (attrDic.TryGetValue("fieldwrapper", out fieldWrapper))
            {
                if (fieldWrapper == null) fieldWrapper = false;
                attrDic.Remove("fieldwrapper");
            }
            else
            {
                fieldWrapper = true;
            }

            var hasFieldWrapper = (Boolean) fieldWrapper;
            if (hasFieldWrapper)
            {
                htmlWriter.WriteBeginTag("div");
                htmlWriter.WriteAttribute("class", "form-field" + (fieldClass != null ? " " + fieldClass : String.Empty));
                htmlWriter.Write(">");
            }

            if (fieldLabel != null)
            {
                htmlWriter.WriteBeginTag("label");
                htmlWriter.WriteAttribute("data-bind", "assoc: true");
                htmlWriter.WriteAttribute("class", "form-field-label" + (labelClass != null ? " " + labelClass : String.Empty));
                htmlWriter.Write(">");
                htmlWriter.WriteEncodedText(fieldLabel.ToString());
                htmlWriter.WriteEndTag("label");
            }

            var hasFieldContainer = (Boolean) fieldContainer;

            if (hasFieldContainer)
            {
                htmlWriter.WriteBeginTag("div");
                htmlWriter.WriteAttribute("class", "form-field-container" + (containerClass != null ? " " + containerClass : String.Empty));
                htmlWriter.Write(">");
            }

            htmlWriter.WriteBeginTag(tag.Name);
            foreach (var attribute in attrDic)
            {
                htmlWriter.WriteAttribute(attribute.Key, attribute.Value == null ? String.Empty : attribute.Value.ToString());
            }
            if (innerHtml == null && innerText == null && tag.SelfClosing)
            {
                htmlWriter.Write("/>");
            }
            else
            {
                htmlWriter.Write(">");
                if (innerHtml != null)
                    htmlWriter.Write(innerHtml.ToString());
                else if (innerText != null)
                    htmlWriter.WriteEncodedText(innerText.ToString());
                htmlWriter.WriteEndTag(tag.Name);
            }

            if (hasFieldContainer)
            {
                htmlWriter.WriteEndTag("div");
            }
            if (hasFieldWrapper)
            {
                htmlWriter.WriteEndTag("div");
            }
            return new HtmlString(stringWriter.GetStringBuilder().ToString());
        }
    }
}
